#include "fileReupload.h"

#include <vector>
#include <string>
#include "public_define.h"
#include "dat_parser.h"
#include "eventloop.h"
#include "adr_component.h"
#include "encode_srv.pb.h"
#include "adr_uploadstatus.pb.h"
#include "tool.h"
#include <set>

#include "common_opr_system.h"
#include "file_task.h"
using namespace DcAdr;

FileReupload::FileReupload(/* args */)
{
}

FileReupload::~FileReupload()
{
}

FileReupload &FileReupload::getInstance()
{
    static FileReupload queue;
    return queue;
}

void FileReupload::setReuploadStatus(FILEREUPLOAD_STATUS status)
{
    m_reuploadSts = status;
}

bool FileReupload::readReuploadFileJson(string &jsonString, Json::Value &root)
{
    // 配置文件若不存在,创建
    if(!CommonOprSystem::isPathExist(global_data::g_file_reupload_path))
    {
        string tmpNull;
        CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, tmpNull);
    }

    // 读取配置文件内容
    CommonOprSystem::readFileAllOnce(global_data::g_file_reupload_path, jsonString);

    if (jsonString.empty())
    {
        return false;
    }

    CommonOprJson::jsonstrformat(jsonString);

    Json::Reader reader;
    //解析不成功或者根节点不是数组就有问题
    if ((!reader.parse(jsonString, root)) || (!root.isArray())) 
    {
        string tmpNull;
        CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, tmpNull);
        Log_Info("[FileReupload][readReuploadFileJson] json parse error! jsonString: {}", jsonString);
        return false;
    }
    return true;
}

void FileReupload::writeFileInfo(const string &filePath, const string &taskId, const int level, const string &eventCode,const string &businessType)
{
    // 保证读写在一个锁内完成
    lock_guard<mutex> locker(mutex_writeReuploadFile);

    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    bool ret = readReuploadFileJson(jsonString, root);

    if (!jsonString.empty() && ret)
    {
        // 若配置文件中已包含该文件的信息,不重复添加
        for (int i = 0; i < root.size(); ++i)
        {
            string readFilePath = JsonToValue<std::string>(root[i]["filePath"]);
            if (!(filePath.compare(readFilePath)))
            {
                Log_Info("[FileReupload][writeFileInfo] {} is exist in {}", filePath, global_data::g_file_reupload_path);
                return;
            }
        }
    }

    Json::Value oneFile;
    // 必填参数
    oneFile["fileName"] = Util::getFileBaseName(filePath);  // 文件名
    oneFile["filePath"] = filePath;                         // 完整路径
    oneFile["taskId"] = taskId;                             // 任务ID
    oneFile["level"] = level;                               // 优先级级别
    oneFile["eventCode"] = eventCode;                       // 事件代码
    oneFile["reuploadTimes"] = 0;                           // 重新上传次数
    oneFile["isChunkFile"] = false;                         // 是否分片文件
    oneFile["businessType"] = businessType;                 // 业务类型

    // 可选参数(若分片)
    // 文件总大小
    uint64_t fileSize = CommonOprSystem::getFileSizeDigit(filePath);
    if (fileSize >= global_data::g_file_chunk_size)
    {
        // 总分片次数
        int totalChunkTime = fileSize / global_data::g_per_chunk_size;
        if ((fileSize % global_data::g_per_chunk_size) > 0)
        {
            totalChunkTime++;
        }
        // 完整文件MD5码
        string filemd5str;
        filemd5str.clear();
        //CommonOprOpenssl::GetFile_Md5_Hex_Lower(filePath, filemd5str);

        oneFile["isChunkFile"] = true;
        oneFile["fileSize"] = fileSize;             // 文件总大小
        oneFile["totalChunkTime"] = totalChunkTime; // 总分片数
        oneFile["curChunkTime"] = 0;                // 当前分片索引
        oneFile["fileMd5"] = filemd5str;            // 文件MD5（注释掉了）
        oneFile["chunkOperation"] = false;          // 分片操作状态
        oneFile["queryTimes"] = 0;                  // 查询次数
    }

    root.append(oneFile);
    std::string writeStr = root.toStyledString();

    Log_Info("[FileReupload][writeFileInfo] root: {}", writeStr);

    CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, writeStr);
}

void FileReupload::writeFileHandle(const string &filePath, const string &fileHandle)
{
    // 保证读写在一个锁内完成
    lock_guard<mutex> locker(mutex_writeReuploadFile);

    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root))
    {
        return;
    }

    for (int i = 0; i < root.size(); ++i)
    {
        string readFilePath = JsonToValue<std::string>(root[i]["filePath"]);
        if (!(filePath.compare(readFilePath)))
        {
            root[i]["fileHandle"] = fileHandle;
            break;
        }
    }
    std::string writeStr = root.toStyledString();
    CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, writeStr);
}

void FileReupload::writeChunkFileMd5(const string &filePath, const string &chunkFileMd5)
{
    // 保证读写在一个锁内完成
    lock_guard<mutex> locker(mutex_writeReuploadFile);

    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root))
    {
        return;
    }

    for (int i = 0; i < root.size(); ++i)
    {
        string readFilePath = JsonToValue<std::string>(root[i]["filePath"]);
        if (!(filePath.compare(readFilePath)))
        {
            // 分片数加1
            int curChunkTime = JsonToValue<int>(root[i]["curChunkTime"]);
            curChunkTime++;
            root[i]["curChunkTime"] = curChunkTime;

            // 文件MD5码信息
            root[i]["fileMd5Arrary"].append(chunkFileMd5);
            break;
        }
    }
    std::string writeStr = root.toStyledString();
    CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, writeStr);
}

void FileReupload::writeQueryTimes(const string &filePath)
{
    // 保证读写在一个锁内完成
    lock_guard<mutex> locker(mutex_writeReuploadFile);

    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root))
    {
        return;
    }

    for (int i = 0; i < root.size(); ++i)
    {
        string readFilePath = JsonToValue<std::string>(root[i]["filePath"]);
        if (!(filePath.compare(readFilePath)))
        {
            // 查询次数加1
            int queryTime = JsonToValue<int>(root[i]["queryTimes"]);
            queryTime++;
            root[i]["queryTimes"] = queryTime;
            break;
        }
    }
    std::string writeStr = root.toStyledString();
    CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, writeStr);
}

void FileReupload::writeQueryTimesToZero(const string &filePath)
{
    // 保证读写在一个锁内完成
    lock_guard<mutex> locker(mutex_writeReuploadFile);

    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root))
    {
        return;
    }

    for (int i = 0; i < root.size(); ++i)
    {
        string readFilePath = JsonToValue<std::string>(root[i]["filePath"]);
        if (!(filePath.compare(readFilePath)))
        {
            // 查询次数置0
            root[i]["queryTimes"] = 0;
            break;
        }
    }
    std::string writeStr = root.toStyledString();
    CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, writeStr);
}

void FileReupload::writeChunkOperation(const string &filePath)
{
    // 保证读写在一个锁内完成
    lock_guard<mutex> locker(mutex_writeReuploadFile);

    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root))
    {
        return;
    }

    for (int i = 0; i < root.size(); ++i)
    {
        string readFilePath = JsonToValue<std::string>(root[i]["filePath"]);
        if (!(filePath.compare(readFilePath)))
        {
            // chunkOperation参数置为true
            root[i]["chunkOperation"] = true;
            break;
        }
    }
    std::string writeStr = root.toStyledString();
    CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, writeStr);
}

//上传成功删除补发文件，失败次数++
void FileReupload::dealUploadResult(std::string filePath, uint32_t result)
{
    // 保证读写在一个锁内完成
    lock_guard<mutex> locker(mutex_writeReuploadFile);

    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root) || jsonString.empty())
    {
        return;
    }

    Json::Value rootNew;
    for (int i = 0; i < root.size(); ++i)
    {
        string readFilePath = JsonToValue<std::string>(root[i]["filePath"]);
        if (!(filePath.compare(readFilePath))) //找到了,根据结果：成功删除；失败此时++
        {
            if (result == 0)
            {
            }
            else
            {
                int uploadTime = JsonToValue<int>(root[i]["reuploadTimes"]);
                if (uploadTime >= reuploadTime) // 共重传三次
                {
                }
                else
                {
                    Json::Value fileInfo = root[i];
                    fileInfo["reuploadTimes"] = uploadTime + 1;
                    rootNew.append(fileInfo);
                }
            }
        }
        else
        {
            rootNew.append(root[i]);
        }
    }

    std::string newDesen;
    if (rootNew.empty())
    {
    }
    else
    {
        newDesen = rootNew.toStyledString();
    }

    Log_Info("[FileReupload][dealUploadResult] reupload after: {}", newDesen);

    CommonOprSystem::writeStringBuffToFile(global_data::g_file_reupload_path, newDesen);
    return;
}

bool FileReupload::readOneFileTask(Reupload_Que_Info &retTask)
{
    lock_guard<mutex> locker(mutex_writeReuploadFile);
    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root) || jsonString.empty())
    {
        return false;
    }

    //做排序，将事件优先级高的先上传,用map做自动排序
    std::map<int, Json::Value> sortMap;
    for (int i = 0; i < root.size(); i++)
    {
        Json::Value childV = root[i];
        int level = 10;
        if (childV.isMember("level"))
        {
            level = JsonToValue<int>(childV["level"]);
        }
        if (Contain_Map(sortMap, level))
        {
            continue;
        }
        sortMap.insert({level, childV});
    }

    if (sortMap.size() >= 1)
    {
        Json::Value fileV = sortMap.begin()->second;
        retTask.fileName = JsonToValue<std::string>(fileV["fileName"]);
        retTask.filePath = JsonToValue<std::string>(fileV["filePath"]);
        retTask.taskId = JsonToValue<std::string>(fileV["taskId"]);
        retTask.eventCode = JsonToValue<std::string>(fileV["eventCode"]);
        retTask.reuploadTimes = JsonToValue<int>(fileV["reuploadTimes"]);
        retTask.businessType = JsonToValue<std::string>(fileV["businessType"]);
    }
    else
    {
        return false;
    }

    return true;
}

bool FileReupload::readOneFileJson(const string &filePath, Json::Value &oneFile)
{
    lock_guard<mutex> locker(mutex_writeReuploadFile);
    // 读取配置文件内容
    string jsonString;
    Json::Value root;

    if (!readReuploadFileJson(jsonString, root) || jsonString.empty())
    {
        return false;
    }

    for (int i = 0; i < root.size(); ++i)
    {
        string readFilePath = JsonToValue<string>(root[i]["filePath"]);
        if (!(filePath.compare(readFilePath)))
        {
            oneFile = root[i];
            return true;
        }
    }
    return false;
}

void FileReupload::timerReupload()
{
    if (m_reuploadSts == IDLE)
    {
        setReuploadStatus(REALWORK);

        auto fun = [=]()
        {
            Reupload_Que_Info fileInfo;
            if (readOneFileTask(fileInfo))
            {
                if ((!(fileInfo.filePath.empty())) && (!(fileInfo.taskId.empty())))
                {
                    Log_Info("[FileReupload][timerReupload] start timer reupload; filePath:{}", fileInfo.filePath);

                    FileTask tmp;
                    FileUploadParam fileup;
                    fileup.taskId = fileInfo.taskId;
                    fileup.filePath = fileInfo.filePath;
                    fileup.fileName = fileInfo.fileName;
                    fileup.eventCode = fileInfo.eventCode;  
                    fileup.businessType = fileInfo.businessType;
                    //tmp.upload(fileup);
                    tmp.s3_upload(fileup);
                }
                else //读到的文件不满足上传要求，删掉
                {
                    Log_ERROR("[FileReupload][timerReupload] read OneFileTask not match; filename:{} taskId:{}", fileInfo.fileName, fileInfo.taskId);
                    dealUploadResult(fileInfo.filePath, 0);
                }
            }

            setReuploadStatus(IDLE);
        };
        appendEvent(ThreadType::E_InFileUploadThread, fun);
    }
}
